package com.zhan.easyexcelorder.service.impl;

import com.google.common.collect.Maps;
import com.zhan.easyexcelorder.model.SysMenu;
import com.zhan.easyexcelorder.model.SysRole;
import com.zhan.easyexcelorder.model.SysUser;
import com.zhan.easyexcelorder.repository.SysMenuRepository;
import com.zhan.easyexcelorder.repository.SysRoleRepository;
import com.zhan.easyexcelorder.repository.SysUserRepository;
import com.zhan.easyexcelorder.service.SysMenuService;
import com.zhan.jpa.service.impl.AbstractServiceImpl;
import com.zhan.utils.Constants;
import com.zhan.utils.page.QueryCondition;
import com.zhan.utils.page.QueryItem;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

@Service
public class SysMenuServiceImpl extends AbstractServiceImpl<SysMenuRepository, SysMenu> implements SysMenuService {

    @Autowired
    private SysUserRepository sysUserRepository;

    @Autowired
    private SysRoleRepository sysRoleRepository;

    @Value(value = "${spring.application.name}")
    private String serverName;

    @Override
    @Cacheable(key = "#userId+#viewTypes[1]", cacheNames = "sysMenu")
    public List<SysMenu> selectSysMenuByUserIdAndViewType(Long userId, List<Integer> viewTypes) {
        StringBuffer hql = new StringBuffer("select m from SysUser as u join u.roles r join r.sysMenus m where m.isDelete=:isDelete and m.menuType=:menuType");
        HashMap<String, Object> params = Maps.newHashMapWithExpectedSize(3);
        params.put("isDelete", Constants.NOT_DEL_VALUE);
        params.put("menuType", "MENU");
        if (Objects.nonNull(userId) && 0L != userId.longValue()) {
            hql.append(" and u.id=:userId");
            params.put("userId", userId);
        }
        if (Objects.nonNull(viewTypes) && !viewTypes.isEmpty()) {
            hql.append(" and m.viewType in (:viewType)");
            params.put("viewType", viewTypes);
            if (viewTypes.contains(Constants.SysMenuType.WEB)) {
                hql.append(" and m.parent is null");
            }
        }

        TypedQuery<SysMenu> query = entityManager.createQuery(hql.toString(), SysMenu.class);
        params.forEach((key, value) -> {
            query.setParameter(key, value);
        });
        List<SysMenu> resultList = query.getResultList();
        return removeButton(resultList);
    }

    private List<SysMenu> removeButton(List<SysMenu> resultList) {
        if (Objects.nonNull(resultList)) {
            Iterator<SysMenu> it = resultList.iterator();
            while (it.hasNext()) {
                SysMenu sysMenu = it.next();
                if (!"MENU".equals(sysMenu.getMenuType())) {
                    it.remove();
                } else {
                    if (StringUtils.isNotBlank(sysMenu.getPhoneMenuIcon())) {
                        sysMenu.setPhoneMenuIcon(serverName + "/image/" + sysMenu.getPhoneMenuIcon());
                    }
                    removeButton(sysMenu.getChildren());
                }
            }
        }
        return resultList;
    }

    @Override
    protected void createPredicate(Root<SysMenu> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder, List<Predicate> list, QueryCondition condition) {
        list.add(criteriaBuilder.isNull(root.get("parent").as(SysMenu.class)));
    }

    @Override
    protected void changeListResult(QueryCondition condition, Page<SysMenu> page) {
        List<QueryItem> query = condition.getQuery();
        List<Long> sysMenuIds = new ArrayList<>();
        if (Objects.nonNull(query)) {
            query.forEach(q -> {
                String column = q.getColumn();
                Object value = q.getValue();
                if (Objects.nonNull(value)) {
                    Long id = Long.parseLong(value.toString());
                    if ("userId".equals(column)) {
                        SysUser sysUser = sysUserRepository.findById(id).get();
                        Set<SysRole> roles = sysUser.getRoles();
                        if (Objects.nonNull(roles) && !roles.isEmpty()) {
                            roles.forEach((role) -> sysMenuIds.addAll(getMenuIdsByRole(role)));
                        }
                    } else if ("roleId".equals(column)) {
                        SysRole sysRole = sysRoleRepository.findById(id).get();
                        List<SysMenu> sysMenus = sysRole.getSysMenus();
                        if (Objects.nonNull(sysMenus) && !sysMenus.isEmpty()) {
                            sysMenus.forEach((menu) -> sysMenuIds.add(menu.getId()));

                        }
                    }
                }
            });
        }

        if (!sysMenuIds.isEmpty()) {
            List<SysMenu> content = page.getContent();
            content.forEach((sysMenu) -> this.changeSysMenuCheckedStatus(sysMenu, sysMenuIds));
        }
    }

    /**
     * 递归修改菜单的选中状态
     *
     * @param sysMenu        菜单
     * @param userSysMenuIds 用户菜单列表
     */
    private void changeSysMenuCheckedStatus(SysMenu sysMenu, List<Long> userSysMenuIds) {
        if (userSysMenuIds.contains(sysMenu.getId())) {
            sysMenu.setChecked(true);
        }
        List<SysMenu> children = sysMenu.getChildren();
        if (Objects.nonNull(children) && !children.isEmpty()) {
            children.forEach((sysMenu1) -> this.changeSysMenuCheckedStatus(sysMenu1, userSysMenuIds));
        }
    }

    /**
     * 获取角色所有的菜单id
     *
     * @param sysRole
     * @return
     */
    private List<Long> getMenuIdsByRole(SysRole sysRole) {
        List<Long> result = new ArrayList<>();
        List<SysMenu> sysMenus = sysRole.getSysMenus();
        if (Objects.nonNull(sysMenus) && !sysMenus.isEmpty()) {
            sysMenus.forEach((sysMenu) -> result.add(sysMenu.getId()));
        }
        return result;
    }

}